package aochulai.qisiren;

import baseFx.common.Dynamic;
import baseFx.common.Stream;
import baseFx.common.utils.RandomUtils;
import baseFx.serialization.json.JsonUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class Game {
    /**
     * 游戏标识
     */
    public String id;
    /**
     * 游戏名称
     */
    public String name;
    /**
     * 是否已经游戏开始
     */
    public boolean started;
    /**
     * 暗牌
     */
    public Stream<Integer> anpai = Stream.create();
    /**
     * 明牌
     */
    public Stream<Integer> mingpai = Stream.create();
    /**
     * 所有玩家
     */
    public Stream<Player> players = Stream.create();
    /**
     * 当前等待做出动作的玩家
     */
    public Player waiting;
    /**
     * 最后一个获胜的玩家
     */
    public Player lastwinner;
    /**
     * 上一把出牌
     */
    public Stream<Integer> lastpai = Stream.create();
    /**
     * 游戏获胜玩家
     */
    public Player gamewinner;

    public Game(String name) {
        this.name = name;
        this.id = UUID.randomUUID().toString();
    }

    private void addCmd(Gop gop, Object data) {
        addCmd(gop, data, null);
    }

    private void addCmd(Gop gop, Object data, Player player) {
        String json = JsonUtils.toJson(Dynamic.setNew(s -> {
            s.put("type", gop.type);
            s.put("data", data);
        }));
        if (player != null) {
            player.cmd.add(json);
        } else {
            players.foreach(i -> i.cmd.add(json));
        }
    }

    /**
     * 新玩家加入
     */
    public void add(Player player) {
        if (started) {
            if (players.any(i -> i.id.equalsIgnoreCase(player.id)) == false) {
                throw new RuntimeException("游戏已开始，不能中途加入");
            }
            players.foreach(i -> addCmd(Gop.新增玩家, Dynamic.setNew(s -> {
                s.put("pid", i.id);
                s.put("name", i.name);
                s.put("ready", i.isReady);
            }), player));
            addCmd(Gop.游戏开始, id, player);
            addCmd(Gop.确定出牌顺序, players.map(i -> Dynamic.setNew(s -> {
                s.put("index", i.index);
                s.put("pid", i.id);
            })).toList(), player);
            addCmd(Gop.刷新手牌, player.shoupai, player);
            showPaiCount();
            if (lastpai.any()) {
                addCmd(Gop.出牌内容, Dynamic.setNew(s -> {
                    s.put("pid", lastwinner.id);
                    s.put("pai", lastpai.toList());
                }));
            }
            addCmd(Gop.通知出牌, Dynamic.setNew(s -> {
                s.put("pid", waiting.id);
                s.put("isTake", lastwinner.id.equals(waiting.id));
            }), player);
            return;
        }
        if (players.any(i -> i.id.equalsIgnoreCase(player.id))) {
            players.remove(player);
            players.add(player);
        } else {
            addCmd(Gop.新增玩家, Dynamic.setNew(s -> {
                s.put("pid", player.id);
                s.put("name", player.name);
                s.put("ready", player.isReady);
            }));
            players.add(player);
        }
        players.foreach(i -> addCmd(Gop.新增玩家, Dynamic.setNew(s -> {
            s.put("pid", i.id);
            s.put("name", i.name);
            s.put("ready", i.isReady);
        }), player));
        player.inGame = this;
    }

    /**
     * 离开游戏
     */
    public void leave(Player player) {
        if (started) {
            player.isOffline = true;
        } else {
            players.remove(player);
            player.inGame = null;
        }
        addCmd(Gop.玩家离开, player.id);
    }

    /**
     * 玩家准备状态变更
     */
    public void ready(Player player, boolean isReady) {
        player.isReady = isReady;
        addCmd(Gop.玩家准备状态变更, Dynamic.setNew(s -> {
            s.put("pid", player.id);
            s.put("ready", isReady);
        }));
        if (isReady) {
            if (players.count() == 1) {
                return;
            }
            if (players.all(i -> i.isReady)) {
                startGame();
                fapai(5);
                waiting = players.get(0);
                lastwinner = waiting;
                addCmd(Gop.通知出牌, Dynamic.setNew(s -> {
                    s.put("pid", waiting.id);
                    s.put("isTake", true);
                }));
            }
        }
    }

    private Player nextPlayer() {
        if (waiting.index == players.count() - 1) {
            return players.get(0);
        }
        return players.first(i -> i.index == waiting.index + 1).get();
    }

    public void pass(Player player) {
        if (player.id.equals(waiting.id) == false) {
            throw new RuntimeException("正在等待" + waiting.name + "出牌");
        }
        addCmd(Gop.要不起, player.id);
        waiting = nextPlayer();
        if (lastwinner.id.equals(waiting.id)) {
            lastpai.remove(i -> true);
            fapai(1);
            addCmd(Gop.通知出牌, Dynamic.setNew(s -> {
                s.put("pid", waiting.id);
                s.put("isTake", true);
            }));
        } else {
            addCmd(Gop.通知出牌, Dynamic.setNew(s -> {
                s.put("pid", waiting.id);
                s.put("isTake", false);
            }));
        }
    }

    public void chupai(Player player, Integer[] pai, String[] wang) {
        if (pai.length == 0 && wang.length == 1) {
            throw new RuntimeException("不符合规则");
        }
        if (started == false) {
            throw new RuntimeException("游戏还未开始");
        }
        if (player.id.equals(waiting.id) == false) {
            throw new RuntimeException("正在等待" + waiting.name + "出牌");
        }
        Stream<String> smw = Stream.create(wang).filter(i -> i.contains("_"));
        List<Integer> wp = smw.map(i -> Integer.parseInt(i.split("_")[0])).toList();
        List<Integer> wrp = smw.map(i -> Integer.parseInt(i.split("_")[1])).toList();
        List<Integer> back = new ArrayList<>(player.shoupai);
        for (Integer p : pai) {
            int idx = back.indexOf(p);
            if (idx >= 0) {
                back.remove(idx);
            }
        }
        for (Integer p : wp) {
            int idx = back.indexOf(p);
            if (idx >= 0) {
                back.remove(idx);
            }
        }
        if (player.shoupai.size() - pai.length - wp.size() != back.size()) {
            addCmd(Gop.刷新手牌, player.shoupai, player);
            showPaiCount();
            throw new RuntimeException("手牌验证失败，请重新出牌");
        }
        Integer[] eqlist = Stream.create(pai).add(wrp).toArray(Integer.class);
        if (lastpai.any()) {
            if (Rules.eq(lastpai.toArray(Integer.class), eqlist) == false) {
                throw new RuntimeException("不符合规则");
            }
        } else {
            if (Rules.fmtcrt(eqlist) == false) {
                throw new RuntimeException("不符合规则");
            }
        }
        lastwinner = player;
        lastpai.remove(i -> true);
        lastpai.add(Stream.create(eqlist).toList());
        player.shoupai.clear();
        player.shoupai.addAll(back);
        addCmd(Gop.出牌内容, Dynamic.setNew(s -> {
            s.put("pid", player.id);
            s.put("pai", eqlist);
        }));
        showPaiCount();
        if (player.shoupai.isEmpty()) {
            gameOver();
        } else {
            waiting = nextPlayer();
            addCmd(Gop.通知出牌, Dynamic.setNew(s -> {
                s.put("pid", waiting.id);
                s.put("isTake", false);
            }));
        }
    }

    private void gameOver() {
        started = false;
        gamewinner = waiting;
        players.foreach(i -> i.isReady = false);
        players.remove(i -> i.isOffline);
        addCmd(Gop.游戏结束, Dynamic.setNew(s -> {
            s.put("winner", waiting.id);
            s.put("shoupai", players.filter(i -> !i.id.equals(gamewinner.id)).map(i -> Dynamic.setNew(ps -> {
                ps.put("pid", i.id);
                ps.put("pais", i.shoupai);
            })).toList());
        }));
        waiting = null;
        lastwinner = null;
        lastpai.remove(i -> true);
    }

    /**
     * 从暗牌中获取一张新牌
     */
    private Integer nextAnpai() {
        if (anpai.any() == false) {
            anpai.add(mingpai.sort(i -> UUID.randomUUID()).toList());
            mingpai.remove(i -> true);
        }
        int pai = anpai.get(0);
        anpai = anpai.skip(1);
        return pai;
    }

    /**
     * 更新所有玩家的手牌数
     */
    private void showPaiCount() {
        addCmd(Gop.牌数, players.map(i -> Dynamic.setNew(s -> {
            s.put("pid", i.id);
            s.put("size", i.shoupai.size());
        })).toList());
    }

    /**
     * 发牌
     */
    private void fapai(int num) {
        for (int i = 0; i < num; i++) {
            players.foreach(p -> {
                int pai = nextAnpai();
                p.shoupai.add(pai);
                addCmd(Gop.新牌, pai, p);
            });
        }
        showPaiCount();
    }

    /**
     * 设置玩家出牌顺序
     */
    private void setIndex() {
        Player first;
        if (gamewinner != null && players.any(gamewinner)) {
            first = gamewinner;
        } else {
            first = players.get(Integer.parseInt(RandomUtils.numByRange(0, players.count())));
        }
        List<Player> list = players.toList();
        players.remove(i -> true);
        while (true) {
            Player p = list.get(0);
            if (p.id.equals(first.id)) {
                break;
            } else {
                list.remove(p);
                list.add(p);
            }
        }
        players.add(list);
        players.foreach((i, p) -> {
            p.index = i;
            p.shoupai.clear();
        });
        addCmd(Gop.确定出牌顺序, players.map(i -> Dynamic.setNew(s -> {
            s.put("index", i.index);
            s.put("pid", i.id);
        })).toList());
    }

    /**
     * 初始化暗牌
     */
    private void setAnpai() {
        anpai.remove(i -> true);
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < players.count(); i++) {
            list.addAll(Stream.create(Rules.YiFuPai).toList());
        }
        for (int i = 0; i < list.size(); i++) {
            int r = (int) (Math.random() * list.size());
            list.add(i, list.remove(r));
        }
        anpai.add(list);
    }

    private void startGame() {
        started = true;
        setIndex();
        setAnpai();
        addCmd(Gop.游戏开始, id);
    }

    /**
     * 玩家语音
     */
    public void voice(Player player, String vid) {
        Dynamic data = Dynamic.setNew(s -> {
            s.put("pid", player.id);
            s.put("vid", vid);
        });
        players.filter(i -> i.id.equals(player.id) == false).foreach(i -> {
            addCmd(Gop.玩家语音, data, i);
        });
    }

}
